using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Caching.Memory;
using System.IO;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.WebEncoders;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using System.Text;
using System.Runtime.Loader;
using System.Collections.Specialized;
using System.Text.RegularExpressions;

namespace QQZiFramework.Mvc
{
    public static class Extension
    {


        static IConfiguration _configuration;

        public static IConfiguration Configuration
        {
            get
            {
                if (_configuration == null) throw new Exception("please call services.AddDBContext at startup");
                return _configuration;
            }
        }
        static IWebHostEnvironment _host = null;

        public static IWebHostEnvironment Hosting
        {
            get
            {
                if (_host == null) throw new Exception("please call services.AddDBContext at startup");
                return _host;
            }
        }
        static IServiceProvider _serviceProvider;

        public static IServiceProvider ServiceProvider
        {
            get
            {
                if (_serviceProvider == null) throw new Exception("please call services.AddDBContext at startup");
                return _serviceProvider;
            }

        }

        static IServiceCollection services;
        public static IServiceCollection Services { get { return services; } }

        public static string GetWWWRootPath()
        {
            string filePath;
            if (string.IsNullOrEmpty(Hosting.WebRootPath))
            {
                filePath = Hosting.ContentRootPath;
            }
            else
            {
                filePath = Hosting.WebRootPath;
            }
            return filePath;
        }
        public static string GetAppRootPath()
        {
            string filePath = GetWWWRootPath();
            System.IO.DirectoryInfo directory = new DirectoryInfo(filePath);
            return directory.Parent.ToString();
        }

        public static string MapPath(string path)
        {
            if (string.IsNullOrEmpty(path)) return null;
            if (path[0] == '~') path = path.Substring(1);
            else if (path[0] == '/') path = path.Substring(1);
            string filePath = GetWWWRootPath();
            foreach (string str in path.Replace('\\', '/').Split('/'))
            {
                filePath = Path.Combine(filePath, str);
            }
            return filePath;
        }

        public static void UserDBContext<T>(this IApplicationBuilder app, IWebHostEnvironment env, IConfiguration configuration) where T : QQZiFramework.Mvc.WebHostMemberShip
        {
            if (configuration == null)
            {
                throw new ArgumentNullException(nameof(configuration));
            }
            _configuration = configuration;
            _host = env;
            // IConfigurationSection section = configuration.GetSection("ConnectionStrings");
            // ConnectionStringSettings connSet = new ConnectionStringSettings();
            // connSet.ConnectionString = section.GetSection("DefaultConnection").Value;
            // connSet.ProviderName = section.GetSection("ProviderName").Value;

            T t = (T)Activator.CreateInstance(typeof(T));
            QQZiFramework.DBContextConfig config = new QQZiFramework.DBContextConfig() { EntityTypeFormat = "QQZiCore.Modals.{0},QQZiCore.Modals" };

            IConfigurationSection section = configuration.GetSection("GlobalDBContextConfig");
            if (section != null)
            {
                if (section["ProviderName"] != null) config.DatabaseType = section["ProviderName"];
                if (section["LogSql"] != null) config.LogSql = (QQZiFramework.SqlLogMode)Enum.Parse(typeof(QQZiFramework.SqlLogMode), section["LogSql"]);
                else config.LogSql = QQZiFramework.SqlLogMode.All;
                if (section["EntityTypeFormat"] != null) config.EntityTypeFormat = section["EntityTypeFormat"];
                if(section["AlwayQueryCount"]  !=null) config.AlwayQueryCount=Convert.ToBoolean(section["AlwayQueryCount"] );
            }
            String permissionFile = Path.Combine(QQZiFramework.Mvc.Extension.GetAppRootPath(), "Permission.json");
            if (!File.Exists(permissionFile))
            {
                string oldpermissionFile = WebHostMemberShip.MapPath("Permission.json");
                if (File.Exists(oldpermissionFile))
                {
                    File.Move(oldpermissionFile, permissionFile);
                }
            }
            //String logFile = WebHostMemberShip.MapPath("log4net.config");
            DBContext.LoadConfig(config);
            DBContext.RegistMembership(t);

            // if (!string.IsNullOrEmpty(section.GetSection("LogConnection").Value))
            // {
            //     ConnectionStringSettings logConnSet = new ConnectionStringSettings();
            //     logConnSet.ConnectionString = section.GetSection("LogConnection").Value;
            //     logConnSet.ProviderName = connSet.ProviderName;
            //     DBContext.LogDBConfig = new DBContextConfig(logConnSet, "QQZiCore.Modals.{0},QQZiCore.Modals");
            // }
            String redisIp = configuration["Redis:IP"];
            if (!string.IsNullOrEmpty(redisIp))
            {
                RedisConfig redisConfig =new RedisConfig();
                configuration.GetSection("Redis").Bind(redisConfig);

                // String redisPort = configuration["Redis:Port"];
                // String redisPool = configuration["Redis:Pool"];
                // String redisPassword = configuration["Redis:Password"];
                // bool redisLog = false;
                // if(configuration["Redis:Log"]!=null){
                //     redisLog = Convert.ToBoolean(configuration["Redis:Log"]);
                // }
                // int redisPoolInt = 100;
                // if (!string.IsNullOrEmpty(redisPool))
                // {
                //     redisPoolInt = Convert.ToInt32(redisPool);
                // }
                RedisAccess.SetRedis(redisConfig);
            }
            IConfigurationSection wxSection = configuration.GetSection("Weixin");
            if (wxSection != null)
            {
                //微信配置
                //LoginCheckFilterAttribute.WEIXIN_AUTHORIZE = wxSection.GetSection("Authorize").Value == "True";
                // GetAccessToken.appId = wxSection.GetSection("AppId").Value;
                // GetAccessToken.appSecret = wxSection.GetSection("AppSecret").Value;
                // GetAccessToken.weixinAccount = wxSection.GetSection("Account").Value;
                // GetAccessToken.weixinName = wxSection.GetSection("Name").Value;
                // string WeixinAuthorizeUrl = wxSection.GetSection("AuthorizeUrl").Value; ;
                // if (!string.IsNullOrEmpty(WeixinAuthorizeUrl)) GetAccessToken.authorizeUrl = WeixinAuthorizeUrl;
            }

            string rootPath = GetWWWRootPath();
            ConvertHelper.init(rootPath);

        }
        static IMvcBuilder mvcBuilder;
        public static void AddQQZiMVC(this IServiceCollection s, System.Reflection.Assembly[] moduleAssemblys)
        {
            services.Configure<KestrelServerOptions>(options =>
            {
                options.AllowSynchronousIO = true;
                //设置应用服务器Kestrel请求体最大为50MB
                options.Limits.MaxRequestBodySize = 52428800;
            });
            services.Configure<IISServerOptions>(options =>
            {
                options.AllowSynchronousIO = true;
            });
            services.Configure<WebEncoderOptions>(options =>
             {
                 options.TextEncoderSettings = new TextEncoderSettings(System.Text.Unicode.UnicodeRanges.All);
             });
            services.AddDistributedMemoryCache();
            services.AddSession();
            mvcBuilder = services.AddControllers().AddNewtonsoftJson(options =>
              {
                  //忽略循环引用
                  //options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
                  //不使用驼峰样式的key
                  options.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver();
                  //设置时间格式
                  options.SerializerSettings.DateFormatString = "yyyy-MM-dd";
              });
            // .AddMvcOptions(options =>
            // {
            //     options.Filters.Add<UnCatchErrorAttribute>();
            // });
            services.AddMvc(options => { options.EnableEndpointRouting = false; });

            //加载模块dll中的控制器
            if (moduleAssemblys != null)
            {
                foreach (var assembly in moduleAssemblys)
                {
                    mvcBuilder.AddApplicationPart(assembly);
                }
            }
        }


        public static void AddUnCatchFilter<T>(this IServiceCollection services)
        {
            mvcBuilder.AddMvcOptions(options =>
            {
                options.Filters.Add(typeof(T));
            });
        }


        public static void AddDBContext(this IServiceCollection s)
        {
            services = s;
            // public IMemoryCache Cache;Cache = new MyMemoryCache(new MyMemoryCacheOptions());
            services.AddSingleton<IMemoryCache, MyMemoryCache>();
            //services.TryAddSingleton<Microsoft.AspNetCore.Http.IHttpContextAccessor, QQZIHttpContextAccessor>();
            services.AddSingleton(typeof(Microsoft.AspNetCore.Http.IHttpContextAccessor), typeof(QQZIHttpContextAccessor));
            services.AddSingleton<Microsoft.AspNetCore.Mvc.Infrastructure.IActionContextAccessor, Microsoft.AspNetCore.Mvc.Infrastructure.ActionContextAccessor>();
            
        }

        public static void BuildServiceProvider4QQZiFramework(this IServiceCollection s)
        {
            services = s;
            DBContext.ServiceProvider = services.BuildServiceProvider();
        }

        public static int Session_Redis_Database_Index;


        public static HttpContext GetHttpContext(this DBContext context)
        {
            HttpContext http = context.GetArgument<Microsoft.AspNetCore.Http.HttpContext>("HttpContext");
            if (http == null) throw new Exception("请在初始化dbcontext实例的时候添加HttpContext");
            return http;
        }

        public static void RemoveSession(this DBContext context, string key)
        {
            //if (context.GetHttpContext() == null) throw new Exception("context.Current is null,please use CreateDbContext()");
            key = context.SessionKey(key);
            context.GetRedisDatabase(Session_Redis_Database_Index).DeleteRedisItem(key);

        }

        private static string SessionKey(this QQZiFramework.DBContext context, string key)
        {
            if (!context.UserDotNetSessionId())
            {
                return GetRedisSessionKey(SessionIDManager.GetInstance().CreateOrGetSessionID(context), key);
            }
            else
            {
                return context.Membership.OnCacheKey(key);
            }
        }

        public static void SetRedisSessionFor(this QQZiFramework.DBContext context, string otherSessionId, string key, object value)
        {
            string fullKey = GetRedisSessionKey(otherSessionId, key);
            SetRedisSession(context, fullKey, value);
        }

        public static void SetDotnetSessionFor(this QQZiFramework.DBContext context, string key, object value)
        {
            string fullKey = context.Membership.OnCacheKey(key);
            SetDotnetSession(context, fullKey, value);
        }

        private static void SetDotnetSession<T>(QQZiFramework.DBContext context, string sessionfullkey, T value)
        {
            HttpContext httpContext = context.GetHttpContext();
            context.LogMessage("cookies的SetSession_Id：" + httpContext.Session.Id);
            Type type = typeof(T);
            string itemJson;
            if (type.IsValueType || type == typeof(string))
            {
                itemJson = value.ToString();
            }
            else
            {
                itemJson = JsonConvert.SerializeObject(value);
            }
            context.GetHttpContext().Session.Set(sessionfullkey, Encoding.UTF8.GetBytes(itemJson));
        }

        private static string GetRedisSessionKey(string sessionId, string key)
        {
            if (sessionId.Length > 48)//早期的guid 36位
            {
                sessionId = Utility.Decrypt(System.Web.HttpUtility.UrlDecode(sessionId));
            }
            return sessionId + "_" + key;
        }


        private static void SetRedisSession(QQZiFramework.DBContext context, string sessionfullkey, object value)
        {
            context.LogMessage("redis的SetSession_Id：" + sessionfullkey);
            int minute = context.GetSettingValue<int>("SessionTimeout", false, 20);
            context.GetRedisDatabase(Session_Redis_Database_Index).SetRedisItem(sessionfullkey, value, DateTime.Now.AddMinutes(minute));

        }

        public static void SetSession<T>(this QQZiFramework.DBContext context, string key, T value)
        {
            HttpContext httpContext = context.GetHttpContext();
            if (httpContext == null) throw new Exception("context.Current is null,please use CreateDbContext()");
            key = context.SessionKey(key);
            if (!context.UserDotNetSessionId())
            {
                SetRedisSession(context, key, value);
            }
            else
            {

                SetDotnetSession(context, key, value);
            }
        }

        // public static string GetSessionId(this QQZiFramework.DBContext context)
        // {
        //     string key = SessionKey(context, "");
        //     return key.Substring(0, key.Length - 1);
        // }

        public static T GetSession<T>(this QQZiFramework.DBContext context, string sessionId, string key)
        {
            HttpContext httpContext = context.GetHttpContext();
            return context.GetRedisDatabase(Session_Redis_Database_Index).GetRedisItem<T>(sessionId + "_" + key, false);
        }

        /// <summary>
        /// 是否使用自带的session
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public static bool UserDotNetSessionId(this QQZiFramework.DBContext context)
        {
            HttpContext httpContext = context.GetHttpContext();
            bool useSessionId = context.GetSettingValue<bool>("Basic:UseSessionId", false);//是否使用自带的session，默认false(使用redis)
            if (useSessionId && httpContext.Session == null) return false;//如果httpContext没有session，比如app，则使用redis
            if (!useSessionId)
            {
                if (SessionIDManager.GetInstance().FromRequest(context) == null)
                {
                    //如果请求中获不到session，则使用默认的
                    useSessionId = true;
                }
            }
            return useSessionId;
        }

        public static T GetSession<T>(this QQZiFramework.DBContext context, string key)
        {
            HttpContext httpContext = context.GetHttpContext();
            key = context.SessionKey(key);

            if (!context.UserDotNetSessionId())
            {
                context.LogMessage("redis的GetSession_Id：" + key);
                return context.GetRedisDatabase(Session_Redis_Database_Index).GetRedisItem<T>(key, false);
            }
            else
            {
                context.LogMessage("cookies的GetSession_Id：" + httpContext.Session.Id);
                Type type = typeof(T);
                T itemJson;
                byte[] bytes = httpContext.Session.Get(key);
                if (bytes == null || bytes.Length == 0) return default(T);
                string str = Encoding.UTF8.GetString(bytes);
                if (type.IsValueType || type == typeof(string))
                {
                    itemJson = (T)Convert.ChangeType(str, typeof(T));
                }
                else
                {
                    itemJson = JsonConvert.DeserializeObject<T>(str);
                }
                return itemJson;
            }
        }



        /// <summary>
        /// 图片上传，转传到图片服务器
        /// </summary>
        /// <param name="rediretSaveAction">转存服务地址</param>
        /// <param name="limitSize">上传大小限制，默认10M</param>
        /// <returns></returns>        
        public static JsonMessage UploadImage(this IQQZiController controller, string rediretSaveAction, bool isTemp, int limitSize = 10485760)
        {
            DBContext context = ((IQQZiController)controller).CreateDBContext();
            WebHostMemberShip memberShip = (WebHostMemberShip)context.Membership;
            HttpRequest Request = ((Microsoft.AspNetCore.Mvc.ControllerBase)controller).Request;
            JsonMessage jm;
            string disposition = Request.Headers["HTTP_CONTENT_DISPOSITION"];
            //转存到文件服务器
            string Authority = Request.Host.ToString();
            if (!string.IsNullOrEmpty(rediretSaveAction) && rediretSaveAction.IndexOf(Authority) == -1)
            {
                if (isTemp && rediretSaveAction.IndexOf("isTemp") == -1) rediretSaveAction += "?isTemp=1";
                Request.Body.Seek(0, SeekOrigin.Begin);
                using (var buffer = new MemoryStream())
                {
                    Request.Body.CopyTo(buffer);
                    buffer.Position = 0;
                    if (buffer.Length == 0) throw new ClientException("没有要上传的内容");
                    byte[] bytes = new byte[buffer.Length];
                    buffer.Read(bytes, 0, bytes.Length);
                    MyHttpUtility http = context.NewHttpUtility();
                    NameValueCollection headers = new NameValueCollection();
                    if (disposition != null)
                        headers.Add("Content-Disposition", disposition);
                    string backStr = http.DoPost(rediretSaveAction, bytes, Request.ContentType, headers, null);
                    jm = JsonConvert.DeserializeObject<JsonMessage>(backStr);
                }




            }
            else
            {
                jm = new JsonMessage();
                int maxattachsize = limitSize; // 最大上传大小，默认是10M
                //保存文件的物理路径
                Random r = new Random();
                string shortTime = DateTime.Now.ToString("yyyy/MM/dd") + "/";
                string dicName = isTemp ? "Temp" : "Upload";
                string filePhysicalPath = QQZiFramework.Mvc.Extension.MapPath("~/Content/" + dicName + "/" + shortTime);
                if (!Directory.Exists(filePhysicalPath)) //判断上传文件夹是否存在，若不存在，则创建
                {
                    Directory.CreateDirectory(filePhysicalPath); //创建文件夹
                }
                string fileName;
                //直接ajax传base64字符串上来的
                string base64Str = controller.GetParamValue("base64Str", int.MaxValue, false, false);
                if (!string.IsNullOrEmpty(base64Str))
                {

                    int posEnd = base64Str.IndexOf(';', 11, 5);
                    if (posEnd == -1) throw new ClientException("扩展名错误!");
                    string ext = "." + base64Str.Substring(11, posEnd - 11);
                    if (!WebHostMemberShip.CheckFileExtension(ext)) throw new ClientException("文件为不可上传的类型.");
                    fileName = Guid.NewGuid().ToString() + ext;
                    base64Str = base64Str.Substring(posEnd + 8);
                    byte[] imageBytes = Convert.FromBase64String(base64Str);
                    if (imageBytes.Length > maxattachsize) throw new ClientException("文件大小超过" + maxattachsize + "字节");
                    string saveFile = System.IO.Path.Combine(filePhysicalPath, fileName);
                    System.IO.FileStream fs = new System.IO.FileStream(saveFile, System.IO.FileMode.Create, System.IO.FileAccess.Write);
                    fs.Write(imageBytes, 0, imageBytes.Length);
                    fs.Flush();
                    fs.Close();
                }
                else
                {

                    if (disposition != null)
                    {
                        // HTML5上传
                        Request.Body.Seek(0, SeekOrigin.Begin);
                        using (var buffer = new MemoryStream())
                        {
                            Request.Body.CopyTo(buffer);
                            buffer.Position = 0;
                            byte[] bytes = new byte[buffer.Length];
                            buffer.Read(bytes, 0, bytes.Length);
                            if (buffer.Length == 0) throw new ClientException("没有要上传的内容");
                            if (buffer.Length > maxattachsize) throw new ClientException("文件大小超过" + maxattachsize + "字节");
                            string localname = System.Net.WebUtility.UrlDecode(Regex.Match(disposition, "filename=\"(.+?)\"").Groups[1].Value); // 读取原始文件名
                            fileName = Guid.NewGuid().ToString() + System.IO.Path.GetExtension(localname);
                            string saveFile = System.IO.Path.Combine(filePhysicalPath, fileName);
                            System.IO.FileStream fs = new System.IO.FileStream(saveFile, System.IO.FileMode.Create, System.IO.FileAccess.Write);
                            fs.Write(bytes, 0, bytes.Length);
                            fs.Flush();
                            fs.Close();
                        }
                    }
                    else
                    {
                        Request.Body.Seek(0, SeekOrigin.Begin);
                        //保存本地
                        IFormFile images = Request.Form.Files.Count > 0 ? Request.Form.Files[0] as IFormFile : null;
                        //接受上传文件
                        if (images == null)
                        {
                            throw new ClientException("请选择文件");
                        }
                        fileName = images.FileName;
                        //获取文件名
                        string ext = System.IO.Path.GetExtension(fileName);
                        if (string.IsNullOrEmpty(ext) || ext.Length > 5) ext = ".jpg";
                        fileName = Guid.NewGuid().ToString() + ext;

                        //文件不能大于1M
                        if (Request.ContentLength > maxattachsize) throw new ClientException("文件大小超过限制要求.");
                        if (!WebHostMemberShip.CheckFileExtension(ext)) throw new ClientException("文件为不可上传的类型.");
                        string saveFile = System.IO.Path.Combine(filePhysicalPath, fileName);
                        //保存文件
                        using (var stream = new FileStream(saveFile, FileMode.CreateNew))
                        {
                            images.CopyTo(stream);
                            stream.Close();
                        }
                    }
                }
                jm.Result = "Content/" + dicName + "/" + shortTime + fileName;

            }
            return jm;
        }




    }

}